<?php
/**
 * 큐브리드
 *
 * @package php.db
 */


/**
 * 큐브리드 연결 클래스
 *
 * <code>
 *  $cubrid = db_('cubrid', 'testdb', true);
 *
 *  $cubrid->query("insert into testclass values ('aaaa')");
 *  echo $cubrid->getData("select * from testclass");
 * 
 *  $cubrid->close();
 * </code>
 *
 * @package php.db
 */
class Cubrid extends DBClient {

	private $tempStmt;

	private $oid		= null;

	private $cursor		= array();

	private $proc_name	= "";

	private $bind_list	= array();

	/**
	 * 생성자
	 *
	 */
	public function __construct($option = '', $type = 'default') {
		parent::__construct($option, $type);
	}

	/**
	 * DB 연결
	 * 
	 * @return resource 
	 */
	protected function _connect() {
		return cubrid_connect($this->host, $this->port, $this->db, $this->id, $this->pass );
	}

	/**
	 * 실행된 row 수 
	 *
	 * @return int 
	 */
	public function affectedRows() { 
		return cubrid_affected_rows($this->getResult());
	}

	/** 
	 * set 타입 속성에 값 추가 
	 *
	 * @param string $attr  속성이름 
	 * @param mixed $value 속성값 
	 * @param string $oid oid 
	 * @return bool
	 */
	public function addSet($attr, $value, $oid = '') { 
		return cubrid_set_add($this->getConnection(), $this->getOID($oid), $attr, $value);
	}

	/** 
	 *  seq 마지막에 추가
	 *
	 * @param string $attr  속성이름 
	 * @param mixed $value 속성값 
	 * @param string $oid oid 
	 * @return bool
	 */
	public function addSeq($attr, $value, $oid = '') { 
		$size = cubrid_col_size($this->getConnection(), $this->getOID($oid), $attr);

		return $this->insertSeq($attr, $size+1, $value);
	}

	/** 
	 *  oid 해당되는 instance 삭제 
	 *  
	 *  실제로는 데이타베이스의 레코드가 삭제가 됩니다. 
	 *  
	 * @param string $oid oid 
	 * @return bool 
	 */
	public function drop($oid = '') { 
		return cubrid_drop($this->getConnection(), $this->getOID($oid));
	}

	/** 
	 * set 타입 속성에 값 삭제
	 *
	 * @param string $attr  속성이름 
	 * @param mixed $value 속성값 
	 * @param string $oid oid 
	 * @return bool
	 */
	public function dropSet($attr, $value, $oid = '') { 
		return cubrid_set_drop($this->getConnection(), $this->getOID($oid), $attr, $value);
	}

	/**
	 * oid 특정 속성 가지고 오기 
	 * 
	 * @param string|array $attr 속성(필드) 이름 
	 * @param string $oid 속성(필드) 값을 가지고 있는 oid 
	 * @return mixed|array oid 속성 값
	 */
	public function get($attr, $oid = '') { 
		if (is_array($attr)) { 
			$temp = array();

			foreach ($attr as $temp_attr) { 
				$temp[$temp_attr] = cubrid_get($this->getConnection(), $this->getOID($oid), $temp_attr);
			}

			return $temp;
		} else { 
			return cubrid_get($this->getConnection(), $this->getOID($oid), $attr);
		}
	}

	/**
	 * 현재 oid 얻어오기 
	 * 
	 * @param resource $result 쿼리 결과문으로 나온 result 
	 * @return string result에 검색된 oid 
	 *
	 */
	public function getCurrentOID($result) { 
		return cubrid_current_oid($result);
	}



	/**
	 * oid 설정 얻어오기 
	 *
	 * 기본적으로 설정된 oid가 없으면 parameter로 들어간 oid를 반환한다.  
	 * 
	 * @param string $oid  oid 
	 * @return string 
	 */ 
	public function getOID($oid = '') { 
		return (empty($oid)) ? $this->oid : $oid;
	}



	/** 
	 *  oid 속성값 배열로 가지고 오기 
	 * 
	 * @param string $oid oid값 
	 * @return array  
	 */
	public function gets($oid = '') { 
		return cubrid_get($this->getConnection(), $this->getOID($oid));
	}

	/** 
	 *  seq 특정 영역에 넣기 
	 * 
	 * @param string $attr  속성이름 
	 * @param int $index  sequence index , 처음 위치는 1 
	 * @param mixed $value 속성값 
	 * @param string $oid oid 
	 * @return bool
	 */
	public function insertSeq($attr, $index, $value, $oid = '') { 
		return cubrid_seq_insert($this->getConnection(), $this->getOID($oid), $attr, $index, $value);
	}

	/**
	 * oid 존재 여부 확인 
	 *
	 * @param string $oid  체크할 oid 
	 * @return bool  oid 존재하면 true, 아니면 false
	 */
	public function isInstance($oid = '') { 
		return cubrid_is_instance($this->getConnection(), $this->getOID($oid));
	}


	/** 
	 *  oid 속성 변경 
	 * 
	 * @param string $attr 속성(필드)
	 * @param mixed $value 속성값
	 * @param string $oid oid 
	 * @return bool  성공 했으면 true, 아니면 false
	 */
	public function put($attr, $value, $oid = '') { 
		
		$args = array($this->getConnection(), $this->getOID($oid), $attr);


		if (is_array($arr) == false) { 
			$args[] = $value;	
		} 

		return call_user_func_array('cubrid_put', $args); 
	}

	/**
	 * 여러 속성값 동시에 설정 
	 * 
	 * <code>$cubrid->puts(array('aa' => 1, 'bb' => 2, 'cc' => 3), $oid);</code>
	 *
	 * @param array $attr_list 속성리스트
	 * @param string $oid oid 
	 * @return bool
	 */
	public function puts($attr_list, $oid = '') { 
		return cubrid_put($this->getConnection(), $this->getOID($oid), $attr_list);
	}

	/**
	 * oid 설정
	 *
	 * @param string $oid oid 
	 */
	public function setOID($oid) { 
		$this->oid = $oid;
	}




	/** 
	 *  seq 특정영역 삭제 
	 * 
	 * @param string $attr  속성이름 
	 * @param int $index  sequence index , 처음 위치는 1 
	 * @param string $oid oid 
	 * @return bool
	 */
	public function dropSeq($attr, $index, $oid = '') { 
		return cubrid_seq_insert($this->getConnection(), $this->getOID($oid), $attr, $index);
	}

	/** 
	 *  seq 특정 영역 변경하기 (update)
	 * 
	 * @param string $attr  속성이름 
	 * @param int $index  sequence index , 처음 위치는 1 
	 * @param mixed $value 변경될 값 
	 * @param string $oid oid 
	 * @return bool
	 */
	public function putSeq($attr, $index, $value, $oid = '') { 
		return cubrid_seq_put($this->getConnection(), $this->getOID($oid), $attr, $index, $value);
	}

	/** 
	 * collection 개수 얻어오기 
	 *
	 * 속성이 collection 타입일 때 개수를 얻어온다. 
	 * 
	 * @param string $attr  속성이름 
	 * @param string $oid oid 
	 * @return int 
	 */
	public function getCollectionSize($attr, $oid = '') { 
		return cubrid_col_size($this->getConnection(), $this->getOID($oid), $attr);
	}

	/** 
	 * collection 타입을 배열 형태로 얻어옴 (set, multiset, sequence)
	 *
	 * 속성이 collection 타입일 때 값을 배열로 얻어옵니다. 
	 *
	 * @param string $attr  속성이름 
	 * @param string $oid oid 
	 * @return array
	 */
	public function getCollection($attr, $oid = '') { 
		return cubrid_col_get($this->getConnection(), $this->getOID($oid), $attr);
	}

	/** 
	 * 동일한 타입의 set, multiset, sequence 의 경우 Data 객체로 얻어온다.
	 *
	 * collection 타입중에 사용자정의 클래스(테이블) 형태로 저장되어 있는 것이 있으면 
	 * 그것에 대한 데이타를 DBData 객체로 반환해줍니다. 
	 * 
	 * @param string $attr  속성이름 
	 * @param bool $isOne  한칸 전진 여부 
	 * @param string $oid oid 
	 * @return DBData
	 */
	public function getOIDData($attr, $isOne = false, $oid = '') { 
		$a = $this->getCollection($attr, $this->getOID($oid));

		$data = new DBData($this);

		if (empty($a)) { 
			return $data;
		}


		$data->setFields(array_keys($this->gets($a[0])));
		$data->addField('oid');

		foreach ($a as $oid) { 
			$arr = $this->gets($oid);
			$arr['oid'] = $oid;
			$data->add($arr);
		}

		if ($isOne) { 
			$data->next();
		}

		return $data;
	}

	/** 
	 *  읽기 lock 설정 
	 * 
	 * @param string $oid oid 
	 * @return bool 
	 */
	public function setReadLock($oid = '') { 
		return cubrid_lock_read($this->getConnection(), $this->getOID($oid));
	}

	/** 
	 *  쓰기 lock 설정 
	 * 
	 * @param string $oid oid 
	 * @return bool 
	 */
	public function setWriteLock($oid = '') { 
		return cubrid_lock_write($this->getConnection(), $this->getOID($oid));
	}

	/** 
	 * 연결 닫기 
	 * 
	 * @return bool 
	 */
	public function close() {
		cubrid_disconnect($this->getConnection());
	}


	/** 
	 * commit 
	 *
	 * cubrid 는 기본적으로 트랜잭션을 시작하기 때문에 insert, update, delete는 필히 commit 을 실행해야 합니다. 
	 * select 도 commit을 해주면 좋습니다. 
	 * 
	 * @return bool 
	 */
	public function commit() { 
		cubrid_commit($this->getConnection());
	}

	/**
	 * 실행된 select 쿼리에 대해서 fetch 를 통해 한 행(row)을 리턴한다. 
	 *
	 * 리턴되는 값은 array('필드' => 'ㅁㅁㅁ', ...); 형태를 가진다. 
	 * 
	 * @param resource $result select 결과로 나온 resource
	 * @return array row 데이타 
	 */
	public function fetch($result) {
		return cubrid_fetch($result, CUBRID_ASSOC);
	}

	/** 
	 * result 메모리 해제 
	 *
	 * @param resource $result select의 결과인 result 
	 * @return bool
	 */
	public function free($result) { 
		return cubrid_close_request($result);
	}

	/** 
	 *  에러 메세지 리턴 
	 * 
	 * @return string 에러 메세지 
	 */
	public function error() {
		return cubrid_error_msg($this->getConnection());
	}

	/**
	 * 커서 이동 
	 * 
	 * @param int $count 이동시킬 상대위치
	 * @param string $origin  first, current, last, 기본값은 current 
	 * @return bool
	 */
	public function seek($count, $origin = 'current') { 
		$temp_origin = constant("CUBRID_CURSOR_".strtoupper($origin));

		return cubrid_move_cursor($this->getResult(), $count, $temp_origin);
	}

	/** 
	 * 일반 쿼리 실행 
	 * 
	 * <code>$cubrid->query("insert into tclass values ('a')");</code>
	 *
	 * @param string|resource $query  실행될 쿼리문이나 prepare로 실행된 결과 resource
	 * @param string $option 실행될 쿼리 옵션,  oid, async, all
	 * @return resource $query 실행 resource, 실패하면 false 
	 */
	public function query($query, $option = '') {
	
		$options = strtolower($option);

		if ($option == 'oid') { 
			$option = CUBRID_INCLUDE_OID;
		} else if ($option == 'async') {
			$option = CUBRID_ASYNC;
		} else if ($option == 'all') { 
			$option = CUBRID_ASYNC | CUBRID_INCLUDE_OID;
		}

		if (is_string($query)) {	// 일반 sql 문장
			
			$this->addSql($query);

			$args = array($this->getConnection(), $query);

			if ($option != '') { 
				$args[] = $option;
			}

			$this->setResult(call_user_func_array('cubrid_execute', $args));

			return $this->getResult();

		} else {		// prepare를 이용한 객체 생성 

			$args = array($query);

			if ($option != '') { 
				$args[] = $option;
			}

			call_user_func_array('cubrid_execute', $args);

			$this->setResult($query);

			return $this->getResult();
		}
	}

	/**
	 * 쿼리의 결과로 나온 필드의 개수를 리턴한다.
	 *
	 * @param resource $result select 결과로 나온 resource
	 * @return int 필드개수 
	 */
	public function getFieldCount($result) {
		return cubrid_num_cols($result);
	}

	/**
	 * 쿼리의 결과로 나온 필드의 이름 리스트를 배열로 리턴한다.
	 *
	 * @param resource $result select 결과로 나온 resource
	 * @return array 필드이름 리스트 
	 */
	public function getFieldList($result) { 
		return cubrid_column_names($result);
	}

	/**
	 * 정해진 위치의 필드 이름을 얻어온다. 
	 *
	 * @param resource $result select 결과로 나온 resource
	 * @param int $i 필드 위치, 처음은 0 
	 * @return string 필드이름 
	 */
	public function getFieldName($result, $i) {
		$arr = $this->getFieldList($result);

		return $arr[$i];
	}

	/**
	 * 정해진 위치의 필드 타입을 얻어온다. 
	 *
	 * @param resource $result select 결과로 나온 resource
	 * @param int $i 필드 위치, 처음은 0 
	 * @return string 필드타입
	 */
	public function getFieldType($result, $i) {
		$arr = $this->getFieldTypeList($result);

		return $arr[$i];
	}

	/**
	 * 정해진 위치의 필드타입 리스트을 얻어온다. 
	 *
	 * @param resource $result select 결과로 나온 resource
	 * @return array 필드타입 리스트
	 */
	public function getFieldTypeList($result) {	
		return cubrid_column_types($result);
	}

	/**
	 * select 쿼리에 대해서 페이징된 DBData를 리턴한다. 
	 *
	 * 
	 * @param string $query select 쿼리
	 * @param int $page 현재 페이지  
	 * @param int $count 페이지당 리스트 개수 
	 * @param string $order_type 정렬 형태 , order : order by 로 페이징, group : group by 로 페이징, 없으면 rownum으로 페이징 
	 * @return DBData
	 */
	public function getPageData($query, $page = 1, $count = 10, $order_type = 'order') {
		
		if (!in_array($order_type, array('order', 'group'))) { 
			die('order_type 이 틀렸습니다.(order, group 둘 중 하나만 적어주세요)');
		}
		

		// 시작지점 계산하기 
		$start = ($page-1)*$count+1;
		$end = ($start+$count-1);

		if ($order_type == 'order') { 
			$query .= " for orderby_num() between {$start} and {$end} ";
		} else if ($order_type == 'group') { 
			$query .= " having groupby_num() between {$start} and {$end} ";			
		} else { 
			$query .= " and rownum between {$start} and {$end} ";
		}


		return $this->getData($query, false);
	}

	/**
	 * 각각의 데이타를 쿼리구문에 맞는 문자열로 표시 해준다. 
	 *
	 * <code>
	 *  // '123' 으로 표시 
	 *  echo $cubrid->getTypeString('char', '123');
	 * 
	 *  // 123 으로 표시 
	 *  echo $cubrid->getTypeString('int', '123');
	 * </code>
	 * 
	 * @param string $type 타입 이름 
	 * @param mixed $value 변환될 값 
	 * @param boolean $is_null 널을 체크할 것인지 여부 , true 널체크, false 널 체크 안함 
	 * @return string 
	 */
	public function getTypeString($type, $value, $is_null = false ) { 
		$type = strtolower($type);
		$temp = '';

		switch ($type) { 
			case 'char':
			case 'string':
			case 'varchar':
				$temp = sprintf("'%s'", $this->escape($value));
				break;
			case 'nchar':
			case 'nchar varying':
				$temp = sprintf("N'%s'", $this->escape($value));
				break;
			case 'bit':
			case 'bit varying':
				$temp = sprintf("B'%s'", $this->escape($value));
				break;
			case 'integer': 
			case 'int': 
			case 'smallint':
				$temp = sprintf("%d", intval($value));
				break;
			case 'decimal':
			case 'double precision':
			case 'float':
			case 'monetary':
			case 'numeric':
			case 'real':
				$temp = sprintf("%f", floatval($value));
				break;
			case 'date':
			case 'time':
			case 'timestamp':
				$temp = sprintf("%s'%s'", $type, $this->escape($value));
				break;
			case 'list': 
			case 'multiset': 
			case 'sequence':
			case 'set': 
				$temp = sprintf('{%s}', $value);
				break;
			default :
				$temp = $value;
				break;
		}

		return ($is_null && is_null($value)) ? 'NULL' : $temp;
	}


	/** 
	 * 새로운 glo 클래스를 만들고 oid 리턴 
	 *
	 * @param string $class_name 생성될 cubrid 클래스 이름 , 일반적으로 glo 를 씁니다. 
	 * @param string $file_name 저장될 파일 이름 
	 * @return resource|bool  생성된 인스턴스, 아니면 false를 리턴
	 * @see Cubrid::loadGLO(),Cubrid::printGLO(),Cubrid::saveGLO()
	 */
	 public function newGLO($class_name, $file_name) { 
		return cubrid_new_glo($this->getConnection(), $class_name, $file_name);
	 }

	/** 
	 * glo 인스턴스를 원하는 file_name으로 저장
	 *
	 * @param string $file_name 저장될 파일 이름 
	 * @param resource $oid  glo 인스턴스
	 * @return bool 성공하면 true, 실패하면 false 
	 * @see Cubrid::newGLO(),Cubrid::printGLO(),Cubrid::saveGLO()
	 */
	 public function loadGLO($file_name, $oid = '') { 
		return cubrid_load_from_glo($this->getConnection(), $this->getOID($oid), $file_name);
	 }

	/** 
	 * glo 인스턴스를 표준출력으로 보냄
	 * 
	 * <code>
	 * // 바이너리 그대로 화면에 출력 
	 * $cubrid->printGLO($glo);
	 *
	 * // gif 이미지 화면에 출력 
	 * $cubrid->printGLO($glo, 'image/gif');
	 * </code>
	 * 
	 * @param resource $oid  glo 인스턴스
	 * @param string $content_type  header로 지정될 content-type 값
	 * @return bool 성공하면 true, 실패하면 false 
	 * @see Cubrid::loadGLO(),Cubrid::newGLO(),Cubrid::saveGLO()
	 */
	 public function printGLO($oid = '', $content_type = '' ) {

		if ($content_type) { 
			header("Content-Type : {$content_type}");
		}

		return cubrid_send_glo($this->getConnection(), $this->getOID($oid));
	 }

	/** 
	 * rollback
	 *
	 * @return bool 
	 */
	public function rollback() { 
		cubrid_rollback($this->getConnection());
	}

	/** 
	 * glo 인스턴스에  file_name에 있는 데이타로 수정하기 
	 *
	 * 인스턴스 내용을 바꿉니다.
	 * 
	 * @param string $file_name glo로 저장될 파일명 
	 * @param resource $oid glo 인스턴스 
	 * @return bool 성공하면 true, 실패하면 false 
	 * @see Cubrid::loadGLO(),Cubrid::printGLO(),Cubrid::newGLO()
	 */
	 public function saveGLO($file_name, $oid = '') { 
		return cubrid_save_to_glo($this->getConnection(), $this->getOID($oid), $file_name);
	 }





	/**
	 * 바인딩 변수 사용 가능하게 하는 메소드 
	 * 
	 * 바인딩될 변수는 ? 로 표시한다. 
	 *
	 * <code>
	 * //prepare 기본 사용 예제 
	 * $sql = "select * from board where title = ?";
	 * $number = "내용";
	 *
	 * $stmt = $cubrid->prepare($sql);
	 * $cubrid->bind($stmt, 1, $number);
	 *
	 * echo $cubrid->getData($stmt);   or  $cubrid->query($stmt);
	 * </code>
	 *
	 * @param string $query 실행될 쿼리문 
	 * @param string $option 실행될때 oid를 리턴할 것인지 여부 지정 , 'oid' oid리턴 
	 * @return resource prepare 메소드 결과 resource
	 * @see Cubrid::bind()
	 */
	public function prepare($query, $option = '')  { 
		$this->addSql($query);

		if (strtolower($option) == 'oid') { 
			$option = CUBRID_INCLUDE_OID;
		} 

		$args = array($this->getConnection(), $query);

		if ($option != '') { 
			$args[] = $option;
		}

		$this->setStatement(call_user_func_array('cubrid_prepare', $args));
	} 

	/** 
	 * bind 메소드 
	 *
	 * @param resource $stmt prepare() 결과 resource
	 * @param int $index 바인딩될 변수 위치, 기본값 1 
	 * @param mixed $bind_value 바인딩될 값 
	 * @param string bind_vlaue_type 바인딩될 타입, 큐브리드에서 사용되는 타입명을 그대로 사용한다. 
	 * @return bool
	 * @see Cubrid::prepare()
	 */
	public function bind($index = 1, $bind_value, $bind_value_type = '') { 

		$bind_value_type = strtoupper($bind_value_type);

		$args = array($this->getStatement(), $index, $bind_value);

		if ($bind_value_type != '') { 
			$args[] = $bind_value_type;
		} 

		$temp = call_user_func_array('cubrid_bind', $args);

		return $temp ;
	}

	/** 
	 * prepare 된 statement 실행 
	 * 
	 * @param string|resource $query  실행될 쿼리문이나 prepare로 실행된 결과 resource
	 * @param string $option 실행될 쿼리 옵션,  oid, async, all
	 * @return resource $query 실행 resource, 실패하면 false 
	 */
	public function execute($option = '') {
	
		$options = strtolower($option);

		if ($option == 'oid') { 
			$option = CUBRID_INCLUDE_OID;
		} else if ($option == 'async') {
			$option = CUBRID_ASYNC;
		} else if ($option == 'all') { 
			$option = CUBRID_ASYNC | CUBRID_INCLUDE_OID;
		}

		$args = array($this->getStatement());

		if ($option != '') { 
			$args[] = $option;
		}

		$exec = call_user_func_array('cubrid_execute', $args);

		$this->setResult($this->getStatement());

		return $exec;
	}
	
	/**
	 * 프로시저를 실행할 객체를 얻는다. 
	 * 
	 * 
	 * @see lib/php/db/DBClient#createProc($name)
	 * @return CubridProc 
	 */
	public function createProc($str, $option = 'proc') { 
		return new CubridProc($this, $str, $option);
	}	
}

?>